package com.best4cloud.common.core.exception;

import com.best4cloud.common.core.result.Result;
import com.best4cloud.common.core.result.enums.ResultErrorCode;
import lombok.SneakyThrows;
import lombok.extern.slf4j.Slf4j;
import org.springframework.core.annotation.Order;
import org.springframework.http.HttpHeaders;
import org.springframework.http.HttpMethod;
import org.springframework.http.HttpStatus;
import org.springframework.http.ResponseEntity;
import org.springframework.lang.NonNull;
import org.springframework.lang.Nullable;
import org.springframework.util.CollectionUtils;
import org.springframework.validation.BindException;
import org.springframework.validation.BindingResult;
import org.springframework.validation.FieldError;
import org.springframework.web.HttpRequestMethodNotSupportedException;
import org.springframework.web.bind.MethodArgumentNotValidException;
import org.springframework.web.bind.annotation.ExceptionHandler;
import org.springframework.web.bind.annotation.RestControllerAdvice;
import org.springframework.web.context.request.WebRequest;
import org.springframework.web.servlet.mvc.method.annotation.ResponseEntityExceptionHandler;

import java.util.*;

@Order(value = 0)
@Slf4j
@RestControllerAdvice
public class BestControllerAdvice extends ResponseEntityExceptionHandler {

    @ExceptionHandler(BusinessException.class)
    @SneakyThrows
    public Result<String> handleBusinessException(Throwable throwable) {
        return Result.error(ResultErrorCode.BUSINESS_ERROR, throwable.getMessage());
    }

    @Override
    @NonNull
    protected ResponseEntity<Object> handleHttpRequestMethodNotSupported(HttpRequestMethodNotSupportedException ex, HttpHeaders headers, HttpStatus status, WebRequest request) {

        Set<HttpMethod> supportedMethods = ex.getSupportedHttpMethods();
        if (!CollectionUtils.isEmpty(supportedMethods)) {
            headers.setAllow(supportedMethods);
        }
        return handleExceptionInternal(ex, null, headers, status, request);
    }

    @Override
    @NonNull
    protected ResponseEntity<Object> handleExceptionInternal(@NonNull Exception ex,
                                                             @Nullable Object body,
                                                             @NonNull HttpHeaders headers,
                                                             @NonNull HttpStatus status,
                                                             @NonNull WebRequest request) {
        if (ex instanceof BindException) {
            return ResponseEntity.ok(handlerNotValidException((BindException) ex));
        } else if (ex instanceof MethodArgumentNotValidException) {
            return ResponseEntity.ok(handlerNotValidException(((MethodArgumentNotValidException) ex).getBindingResult()));
        }
        return ResponseEntity.ok(Result.error(ResultErrorCode.BUSINESS_ERROR, ex.getMessage()));
    }

    public Result<String> handlerNotValidException(BindingResult bindingResult) {
        Map<String, Object> maps;
        if (bindingResult.hasErrors()) {
            List<FieldError> fieldErrors = bindingResult.getFieldErrors();
            maps = new HashMap<>(fieldErrors.size());
            fieldErrors.forEach(error -> maps.put(error.getField(), error.getDefaultMessage()));
        } else {
            maps = Collections.emptyMap();
        }
        return Result.error(ResultErrorCode.PARAMETER_ERROR, maps.toString());
    }
}
